<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>如何让红绿灯停下来</title>
</head>
<body>
    <button id="start">开始</button>
    <button id="stop">停止</button>
    <div id="status"></div>
    <script>
        const status = document.getElementById('status');
        const sleep = (ms, signal) => new Promise((resolve, reject) => {
            const timer = setTimeout(resolve, ms);
            signal.addEventListener('abort', () => {
                clearTimeout(timer);
                reject(new DOMException('Aborted', 'AbortError'));
            }, { once: true }); // eventemitter 订阅发布者模式
        })
        const seq = [
            { color: 'red', ms: 1000 },
            { color: 'yellow', ms: 3000 },
            { color: 'green', ms: 2000 }
        ]
        let controller = null;
        async function trafficLight(signal){
            while(!signal.aborted){
                for(const { color, ms } of seq){
                    if(signal.aborted){
                        return;
                    }
                    status.textContent = color;
                    try{
                        await sleep(ms, signal);
                    } catch(e){
                        if(e.name === 'AbortError'){
                            return;
                        }
                        throw e;
                    }
                }
            }
        }
        document.getElementById('start').addEventListener('click', () => {
            if(controller && !controller.signal.aborted){
                return;
            }
            controller = new AbortController();
            trafficLight(controller.signal);
        })
        document.getElementById('stop').addEventListener('click', () => {
            if(controller){
                controller.abort();
                controller = null;
            }
        })
    </script>
</body>
</html>